/*
 * Copyright 2010 - 2011, The PLDesktop Development Team
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */

#ifndef PLJAVA_FILE_H
#define PLJAVA_FILE_H

#include <pltypes.h>
using namespace std;
#include <tools/PLVector.h>
#include <tools/PLFlags.h>
#include <tools/PLRadixTree.h>
#include <filetypes/PLJavaConst.h>
#include <filetypes/PLJavaAttributeInterface.h>
#include <filetypes/PLJavaAttributeStackMapVerificationTypeInfo.h>

namespace PLSDK {

class PLJavaConstTypesUtf8;
class PLJavaConstClassInfo;
class PLJavaFieldInfo;
class PLJavaMethodeInfo;
class PLJavaAttribute;

typedef uint8_t  u1;
typedef uint16_t u2;
typedef uint32_t u4;

/**
 * Base class for the class file format data.
 */
class PLJavaFile : public PLJavaAttributeInterface {
public:
    /**
     * Class file format that exists.
     */
    enum MajorVersion {
        /**
         * J2SE 6.0
         */
        J2SE60      = 0x32,     // 50
        /**
         * J2SE 5.0
         */
        J2SE50      = 0x31,     // 49
        /**
         * JDK 1.4
         */
        JDK14       = 0x30,     // 48
        /**
         * JDK 1.3
         */
        JDK13       = 0x2F,     // 47
        /**
         * JDK 1.2
         */
        JDK12       = 0x2E,     // 46
        /**
         * JDK 1.1
         */
        JDK11       = 0x2D      // 45
    };
    /**
     * The access flags from the class or interface.
     */
    enum AccessFlag {
        /**
         * Declared public; may be accessed from outside its package.
         */
        AccessFlagPublic        = 0x0001,
        /**
         * Declared private; usable only within the defining class.
         */
        AccessFlagPrivate       = 0x0002,
        /**
         * Declared protected; may be accessed within subclasses.
         */
        AccessFlagProtected     = 0x0004,
        /**
         * Declared static.
         */
        AccessFlagStatic        = 0x0008,
        /**
         * Declared final; no subclasses allowed.
         */
        AccessFlagFinal         = 0x0010,
        /**
         * Treat superclass methods specially when invoked by the invokespecial instruction.
         */
        AccessFlagSuper         = 0x0020,
        /**
         * Declared synchronized; invocation is wrapped in a monitor lock.
         */
        AccessFlagSynchronized  = 0x0020,
        /**
         * Declared volatile; cannot be cached.
         */
        AccessFlagVolatile      = 0x0040,
        /**
         * Declared transient; not written or read by a persistent object manager.
         */
        AccessFlagTransient     = 0x0080,
        /**
         * Declared native; implemented in a language other than Java.
         */
        AccessFlagNative        = 0x0100,
        /**
         * Is an interface, not a class.
         */
        AccessFlagInterface     = 0x0200,
        /**
         * Declared abstract; may not be instantiated.
         */
        AccessFlagAbstract      = 0x0400,
        /**
         * Declared strictfp; floating-point mode is FP-strict
         */
        AccessFlagStrict        = 0x0800
    };
    PLDECLAREFLAGS(AccessFlags, AccessFlag);
    /**
     * Constructor.
     *
     * @param address The address for the start of the file.
     */
    PLJavaFile(uintptr_t address);
    /**
     * Destructor.
     */
    virtual ~PLJavaFile();
    /**
     * Get the minor version of the class file.
     *
     * @return The minor version.
     */
    inline u2 minorVersion();
    /**
     * Get the major version of the class file.
     *
     * @return The major version.
     */
    inline u2 majorVersion();
    /**
     * Get a java const element at a index from the const table.
     *
     * @return The const table entry.
     */
    const PLJavaConst *constTableEntry(u2 index);
    /**
     * Get the access flags from the class.
     *
     * @return The access flags.
     */
    inline PLJavaFile::AccessFlags accessFlags();
    /**
     * Get the index of the class in the const table.
     *
     * @return The index.
     */
    inline PLJavaConstClassInfo *classValue();
    /**
     * Get the index of the super class in the const table.
     *
     * @return The index.
     */
    inline PLJavaConstClassInfo *superClassValue();
    /**
     * Get the name information from the const table with a index.
     *
     * @param index The index for the name.
     */
    PLJavaConstTypesUtf8 *nameAtIndex(u2 index);
    /**
     * Get the methode information at a index.
     *
     * @return The methode information.
     */
    PLJavaMethodeInfo *methodeAt(u2 index);
    /**
     * Get the number of methode entry's.
     *
     * @return The number.
     */
    inline u2 methodeCount();
    /**
     * Get the methode information for a methode name.
     *
     * @param name The name of the methode to get the information for.
     * @return The methode information.
     */
    PLJavaMethodeInfo *methode(const char *name);
    /**
     * Add new attribute information to the attributes.
     *
     * @param newAttribute The attribute to add.
     */
    void addAttribute(PLJavaAttribute *newAttribute);
    /**
     * Get the attribute at a index.
     *
     * @param index The index of the element to get.
     * @return The attribute.
     */
    PLJavaAttribute *attributeAt(u2 index);
    /**
     * Get the number of attributes.
     *
     * @return The number.
     */
    inline u2 attributesCount();
    /**
     * Get the field at a index.
     *
     * @param index The index of the element to get.
     * @return The field.
     */
    PLJavaFieldInfo *fieldAt(u2 index);
    /**
     * Get the number of fields.
     *
     * @return The number.
     */
    inline u2 fieldCount();
    /**
     * Run the class init function.
     */
    void runInit();
private:
    inline u1 readUnsignedByte();
    inline u2 readUnsignedShort();
    inline u4 readInt();
    PLJavaAttributeStackMapVerificationTypeInfo *getVerificationTypeInfo(u1 tag);
    void addAttributes(PLJavaAttributeInterface *objectToAdd);
    uintptr_t m_pos;
    u1 *m_data;
    u2 m_minorVersion;
    u2 m_majorVersion;
    PLJavaConst **m_constTable;
    u2 m_constCount;
    PLJavaFile::AccessFlags m_accessFlags;
    PLJavaConstClassInfo *m_class;
    PLJavaConstClassInfo *m_superClass;
    PLVector<PLJavaConstClassInfo *> m_interfaces;
    PLJavaFieldInfo **m_fieldInfos;
    u2 m_fieldCount;
    PLJavaMethodeInfo **m_methodeInfos;
    u2 m_methodeCount;
    PLRadixTree<PLJavaMethodeInfo *> m_methodeCache;
    PLVector<PLJavaAttribute *> m_attributes;
    PLJavaMethodeInfo *m_initMethode;
};

u1 PLJavaFile::readUnsignedByte() {
    u1 ret = m_data[m_pos];
    m_pos++;
    return ret;
}

u2 PLJavaFile::readUnsignedShort() {
    u2 ret = (u2)m_data[m_pos] << 8 | (u2)m_data[m_pos + 1];
    m_pos += 2;
    return ret;
}

u4 PLJavaFile::readInt() {
    u4 ret = (u4)m_data[m_pos] << 24 | (u4)m_data[m_pos + 1] << 16 | (u4)m_data[m_pos + 2] << 8 | (u4)m_data[m_pos + 3];
    m_pos += 4;
    return ret;
}

u2 PLJavaFile::minorVersion() {
    return m_minorVersion;
}

u2 PLJavaFile::majorVersion() {
    return m_majorVersion;
}

PLJavaFile::AccessFlags PLJavaFile::accessFlags() {
    return m_accessFlags;
}

PLJavaConstClassInfo *PLJavaFile::classValue() {
    return m_class;
}

PLJavaConstClassInfo *PLJavaFile::superClassValue() {
    return m_superClass;
}

u2 PLJavaFile::attributesCount() {
    return m_attributes.count();
}

u2 PLJavaFile::methodeCount() {
    return m_methodeCount;
}

u2 PLJavaFile::fieldCount() {
    return m_fieldCount;
}

}

#endif
